<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>预编译</title>
</head>
<body>
    
</body>
<script>
    var a = 2
    console.log(a)
    function fn(a, c){
        console.log(a) // function a() {}
        var a = 123
        console.log(a) // 123
        console.log(c) // function c() {}
        function a() { }
        if(false){
            var d = 678
        }
        console.log(d) // undefined
        console.log(b) // undefined
        var b = function() { }
        console.log(b) // function() {}
        function c() { }
        console.log(c) // function c() {}
    }
    var c = function (){
        console.log("c")
    }
    console.log(c)
    fn(1, 2)

    /* 分析过程如下
        1. 页面产生便创建了GO全局对象（Global Object），也就是window对象
        2. 第一个脚本文件加载
        3. 脚本加载完毕后，分析语法是否合法
        4. 开始预编译，查找变量声明，作为GO的属性，值为undefined；查找函数声明，值为函数体
    */
   // 抽象描述
   // 预编译（脚本代码块script执行前）
   GO = {
       a: undefined,
       c: undefined,
       fn: function(a,c){
            console.log(a)
            var a = 123
            console.log(a)
            console.log(c)
            function a() { }
            if(false){
                var d = 678
            }
            console.log(d)
            console.log(b)
            var b = function() { }
            console.log(b)
            function c() { }
            console.log(c)
       },       
   }
   // 解释执行
   GO = {
       a: 2,
       c: function(){
            console.log("c")
       },
       fn: function(a,c){
            console.log(a)
            var a = 123
            console.log(a)
            console.log(c)
            function a() { }
            if(false){
                var d = 678
            }
            console.log(d)
            console.log(b)
            var b = function() { }
            console.log(b)
            function c() { }
            console.log(c)
       },   
   }

    /* 函数执行前进行预编译
        1. 创建AO对象（Active Object）
        2. 查找函数形参和函数内变量声明，作为AO的属性，值为undefined
        3. 将实参值赋值给形参
        4. 查找函数内函数声明，函数名作为AO的属性，值为函数引用
    */
    // 函数预编译（函数执行前）
    // AO = {
    //     a: undefined, -> 1, -> function a() {}
    //     c: undefined, -> 2, -> function c() {}
    //     d: undefined,
    //     b: undefined,    
    // }

    // 解释执行fn函数
    // AO = {
    //     a: function a() {}, -> 123,
    //     c: function c() {},
    //     d: undefined,
    //     b: undefined, -> function() {}
    // }

    /* 
        注意：
            预编译阶段发生变量声明和函数声明，没有初始化行为（赋值），匿名函数不参与预编译；
            只在解释执行阶段才会进行变量初始化
        两种预编译：脚本代码块script执行前、函数执行前
        预编译小结：
            1. 预编译两个小规则：
                a. 变量声明整体提升（无论声明和调用哪个在前，系统总会把变量声明移到调用前面，注意仅仅只是声明，所以值是undefined）
                b. 函数声明整体提升（无论声明和调用哪个在前，系统总会把函数声明移到调用前面）
            2. 预编译前奏
                a. imply global即任何变量，如果未声明就赋值，则此变量就为全局变量所有
                b. 一切声明的全局变量，全是window对象的属性， var a = 12, 即 window.a = 12
            3. 函数预编译发生在函数执行前一刻
    */
</script>
</html>